package io.task.message.socket.maintain;

import io.task.message.socket.ChannelContext;
import io.task.message.socket.Config;
import io.task.message.socket.lock.LockUtils;
import io.task.message.socket.lock.MapWithLock;
import io.task.message.socket.lock.SetWithLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * 一对多  (userid <--> ChannelContext)<br>
 */
public class Users {
    private static final Logger log = LoggerFactory.getLogger(Users.class);
    /**
     * key: userid
     * value: ChannelContext
     */
    private final MapWithLock<String, SetWithLock<ChannelContext>> mapWithLock = new MapWithLock<>(new HashMap<String, SetWithLock<ChannelContext>>());

    /**
     * 绑定userid.
     *
     * @param userid         the userid
     * @param channelContext the channel context
     * @author tanyaowu
     */
    public void bind(String userid, ChannelContext channelContext) {


        try {
            SetWithLock<ChannelContext> setWithLock = mapWithLock.get(userid);
            if (setWithLock == null) {
                LockUtils.runWriteOrWaitRead("_tio_users_bind__" + userid, this, () -> {
//					@Override
//					public void read() {
//					}

//					@Override
//					public void write() {
//						SetWithLock<ChannelContext> setWithLock = mapWithLock.get(userid);
                    if (mapWithLock.get(userid) == null) {
//							setWithLock = new SetWithLock<>(new HashSet<ChannelContext>());
                        mapWithLock.put(userid, new SetWithLock<>(new HashSet<ChannelContext>()));
                    }
//					}
                });
                setWithLock = mapWithLock.get(userid);
            }
            setWithLock.add(channelContext);

            channelContext.setUserid(userid);
        } catch (Throwable e) {
            log.error("", e);
        }

    }

    /**
     * Find.
     *
     * @param userid the userid
     * @return the channel context
     */
    public SetWithLock<ChannelContext> find(Config config, String userid) {


        return mapWithLock.get(userid);
    }


    public MapWithLock<String, SetWithLock<ChannelContext>> getMap() {
        return mapWithLock;
    }

    /**
     * 解除channelContext绑定的userid
     */
    public void unbind(ChannelContext channelContext) {


        String userid = channelContext.userid;


        try {
            SetWithLock<ChannelContext> setWithLock = mapWithLock.get(userid);
            if (setWithLock == null) {
                log.warn("{}, {}, userid:{}, 没有找到对应的SetWithLock", channelContext.config.getName(), channelContext, userid);
                return;
            }

            setWithLock.remove(channelContext);

            if (setWithLock.size() == 0) {
                mapWithLock.remove(userid);
            }

            channelContext.setUserid(null);
        } catch (Throwable e) {
            log.error(e.toString(), e);
        }
    }

    /**
     * 解除tioConfig范围内所有ChannelContext的 userid绑定
     */
    public void unbind(Config config, String userid) {


        try {
            Lock lock = mapWithLock.writeLock();
            lock.lock();
            try {
                Map<String, SetWithLock<ChannelContext>> m = mapWithLock.getObj();
                SetWithLock<ChannelContext> setWithLock = m.get(userid);
                if (setWithLock == null) {
                    return;
                }

                WriteLock writeLock = setWithLock.writeLock();
                writeLock.lock();
                try {
                    Set<ChannelContext> set = setWithLock.getObj();
                    if (set.size() > 0) {
                        for (ChannelContext channelContext : set) {
                            channelContext.setUserid(null);
                        }
                        set.clear();
                    }

                    m.remove(userid);
                } catch (Throwable e) {
                    log.error(e.getMessage(), e);
                } finally {
                    writeLock.unlock();
                }

            } catch (Throwable e) {
                throw e;
            } finally {
                lock.unlock();
            }
        } catch (Throwable e) {
            log.error(e.toString(), e);
        }
    }
}
